package com.sf.gis.boot.rcboot.config;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSON;
import com.google.gson.Gson;
import freemarker.template.utility.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 80004819
 * @ClassName:
 * @Description:
 * @date 2020年09月11日 15:14:50
 */
@Aspect
@Component
@Slf4j
public class WebLogAspect {


    private final static Gson GSON = new Gson();

    @Value("${HttpLog.aop.enable}")
    private boolean enableHttpAopLog;


    @Pointcut("execution(* com.sf.gis.boot..*.controller.*.*(..))")
    public void annotationPointcut() {

    }


    @Around("annotationPointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;
        if (enableHttpAopLog) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            String url = request.getRequestURL().toString();
            String method = request.getMethod();
            String paramsResult = "";
            //获取请求参数集合并进行遍历拼接
            if ("POST".equals(method)) {
                Map map = getFieldsName(joinPoint);
                paramsResult = GSON.toJson(map);
            } else if ("GET".equals(method)) {
                String queryString = request.getQueryString();
                if (StrUtil.isNotBlank(queryString)) {
                    paramsResult = URLDecoder.decode(queryString, "UTF-8");
                }
            }
            log.info("请求接口==》url:{},请求方式:{},请求参数{}", url, method, paramsResult);
            // result的值就是被拦截方法的返回值
            result = joinPoint.proceed();
            log.info("请求响应结果==》返回值:{}", GSON.toJson(result));
        } else {
            result = joinPoint.proceed();
        }
        return result;
    }


    private Map<String, Object> getFieldsName(JoinPoint joinPoint) throws Exception {
        String classType = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        // 参数值
        Object[] args = joinPoint.getArgs();
        HashMap<String, Object> paramMap = new HashMap();
        if (args != null && args.length > 0) {
            Class<?>[] classes = new Class[args.length];
            for (int k = 0; k < args.length; k++) {
                // 对于接受参数中含有MultipartFile，ServletRequest，ServletResponse类型的特殊处理，我这里是直接返回了null。（如果不对这三种类型判断，会报异常）
                if (args[k] instanceof MultipartFile || args[k] instanceof ServletRequest || args[k] instanceof ServletResponse) {
                    return null;
                }
                if (!args[k].getClass().isPrimitive()) {
                    Class s = args[k].getClass();
                    classes[k] = s == null ? args[k].getClass() : s;
                }
            }
            ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
            // 获取指定的方法，第二个参数可以不传，但是为了防止有重载的现象，还是需要传入参数的类型
            Method method = Class.forName(classType).getMethod(methodName, classes);
            // 参数名
            String[] parameterNames = pnd.getParameterNames(method);
            // 通过map封装参数和参数值
            for (int i = 0; i < parameterNames.length; i++) {
                paramMap.put(parameterNames[i], args[i]);
            }
        }
        return paramMap;
    }


}
